
import cv2 as cv
import serial
import time
import argparse

MSG_REQ_SYNC = 0xAA
MSG_REP_SYNC = 0x55
HEADER_LEN = 4

MSG_SYNCING = 0
MSG_HEADER_ID = 1
MSG_HEADER_PDLEN = 2
MSG_CHECKSUM = 3
MSG_SYNC_DONE = 4

def calc_checksum(data):
    checksum = 0
    for d in data:
        checksum ^= d
    return checksum

def msg_send_request(uart, id, payload):
    data = []
    data.append(MSG_REQ_SYNC)
    data.append(id)
    pd_len = len(payload)
    data.append(pd_len & 0xFF)
    data.append((pd_len >> 8) & 0xFF)
    data.append(calc_checksum(data))
    data.extend(payload)
    uart.write(data)
    uart.flush()

def uart_read(uart, size, timeout):
    timeout = timeout / 1000.0
    start_time = time.time()
    data = []
    while True:
        in_waiting = uart.inWaiting()
        if len(data) + in_waiting > size:
            in_waiting = size - len(data)
        if in_waiting > 0:
            data.extend(list(bytearray(uart.read(in_waiting))))
        if len(data) == size:
            return data
        if time.time() - start_time > timeout:
            raise Exception("timeout")


def msg_recv_response(uart):
    state = MSG_SYNCING
    id = 0
    rep = []
    start_time = time.time()
    while True:
        if state == MSG_SYNCING:

            bdata = uart_read(uart, 1, 200)[0]
            if bdata != MSG_REP_SYNC:
                continue
            rep.append(bdata)
            state = MSG_HEADER_ID
        elif state == MSG_HEADER_ID:
            id = uart_read(uart, 1, 100)[0]
            rep.append(id)
            state = MSG_HEADER_PDLEN
        elif state == MSG_HEADER_PDLEN:
            data = uart_read(uart, 2, 100)
            pd_len = data[0] | (data[1] << 8)
            rep.extend(data)
            state = MSG_CHECKSUM
        elif state == MSG_CHECKSUM:
            checksum = calc_checksum(rep)
            temp = uart_read(uart, 1, 100)[0]
            if checksum != temp:
                raise Exception("check sum error: {}".format(rep))
            state = MSG_SYNC_DONE
        elif state == MSG_SYNC_DONE:
            if pd_len > 0:
                payload = list(bytearray(uart.read(pd_len)))
                if payload != [0x00]:
                    raise Exception("response error:{}".format(payload))
                else:
                    return

def img_to_stream(img):
    data = []
    for i in range(len(img)):
        s = ""
        for j in range(len(img[i])):
            if img[i][j] == 0:
                s += ' '
            else:
                s += '*'
        # print(s)
        width = len(img[i]) >> 3
        for j in range(width):
            tmp = img[i][j * 8: j * 8 + 8]
            value = 0
            for pix in range(8):
                if tmp[pix] & 0x01:
                    value |= 1 << pix
            data.append(value)
    return data

def video_play(file, port, baudrate):
    fps = 30
    cap = cv.VideoCapture('./bad_apple.mp4')

    print("cap is opend: {}".format(cap.isOpened()))
    vfps = cap.get(cv.CAP_PROP_FPS)
    print("FPS:{}".format(cap.get(cv.CAP_PROP_FPS)))
    uart = serial.Serial(port)
    uart.baudrate = baudrate
    data = []
    i = 0
    # print("\33[2J")
    # print("\033[0;0H")
    retry = False
    skip = vfps // fps
    if vfps % fps:
        skip += 4
    skip += 3
    delay = 1.0 / fps
    # print("skip = {}".format(skip))
    while True:
        
        if not retry:
            start_time = time.time()
            retval, img = cap.read()
            time.sleep(0.001)
            i += 1
            if i % skip:
                continue
            img = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
            ret, img = cv.threshold(img, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
            img = cv.resize(img, (128, 64))

            width = img.shape[0]
            height = img.shape[1]
            # print("\033[0;0H")
            # print("height={}, width={}".format(width, height))
            # print("")

            # cv.imshow(file, img)
            data = img_to_stream(img)

        msg_send_request(uart, i & 0xFF, data)
        try:
            msg_recv_response(uart)
            retry = False
            cv.waitKey(1)
        except Exception:
            retry = True
        
        # time.sleep(delay)



if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Play video through oled')
    parser.add_argument('-f', '--file', help='The video file', default="./bad_apple.mp4")
    parser.add_argument('-p', '--port', help='The uart port', default="COM4")
    parser.add_argument('-b', '--baudrate', help='uart baudrate', default=230400)
    args = parser.parse_args()
    video_play(args.file, args.port, args.baudrate)

